package com.codeoss.framework.view.datasource;

import java.sql.Timestamp;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.codeoss.framework.context.UserContext;
import com.codeoss.framework.context.YunContext;
import com.codeoss.framework.data.metadata.Column;
import com.codeoss.framework.data.metadata.Expression;
import com.codeoss.framework.data.metadata.Query;
import com.codeoss.framework.data.metadata.Restrictions;
import com.codeoss.framework.data.metadata.Table;
import com.codeoss.framework.data.metadata.TableData;
import com.codeoss.framework.db.table.ITableService;
import com.codeoss.framework.db.table.store.ITableServiceStore;
import com.codeoss.framework.db.util.ColumnChangeLoggingUtil;
import com.codeoss.framework.db.util.ModelAccessLoggingUtil;
import com.codeoss.framework.expression.JavaScriptScriptService;
import com.codeoss.framework.util.PermissionUtil;
import com.codeoss.framework.util.StringUtils;
import com.codeoss.framework.view.util.TableCRUDRuleUtil;
import com.codeoss.framework.view.util.ValidatorUtil;

@Service("dbDataSource")
public class DBDataSource implements IDataSource {
	@Autowired
	private ITableServiceStore tableServiceStore;
	@Autowired
	private ValidatorUtil validatorUtil;
	@Autowired
	private PermissionUtil permissionUtil;
	@Autowired
	private TableCRUDRuleUtil tableCURDRuleUtil;
	@Autowired
	 private JavaScriptScriptService javaScriptScriptService;
	 @Autowired
	 private ColumnChangeLoggingUtil columnChangeLoggingUtil;
	 @Autowired
	 private ModelAccessLoggingUtil modelAccessLoggingUtil;
	@Override
	public TableData select(YunContext yunContext, Table table, Query query) {
		// TODO Auto-generated method stub
		permissionUtil.hasPermission(table,"select");
		ITableService tableService=tableServiceStore.getTableService();
		String extraSql=tableCURDRuleUtil.doSelectRule(yunContext, table,"select");
		if(!StringUtils.isEmpty(extraSql)) {
			if(query==null) {
				query=new Query();
				query.add(Restrictions.sql(extraSql));
			}else {
				List<Expression> expressionList = query.getExpressionList();
				boolean isExist=false;
				if (expressionList.size() > 0) {
					for (int i = 0; i < expressionList.size(); i++) {
						Expression expression = expressionList.get(i);
						String sql=expression.getSQL();
						if(!StringUtils.isEmpty(sql)) {
							sql="("+sql+") and ("+extraSql+")";
							expression.setSQL(sql);
							isExist=true;
							break;
						}
					}
				}
				if(!isExist) {
					query.add(Restrictions.sql(extraSql));
				}
			}
		}
		
		TableData tableData=tableService.select(yunContext, table, null, query);
		List<Map<String,Object>> data=tableData.getData();
		List<Column> columnList=table.getAllColumnList();
		for(int i=0;i<data.size();i++) {
			Map<String,Object> dataMap=data.get(i);
			for (Column column : columnList) {
				 if(!column.isStoreColumn()) {
					 String defaultValueExpression=column.getDefaultValueExpression();
					 if(!StringUtils.isEmpty(defaultValueExpression)) {
						 Object value=javaScriptScriptService.eval(defaultValueExpression, dataMap);
						 dataMap.put(column.getColumnName(), value);
					 }
				 }
			}
		}
		if(query!=null) {
			tableData.setPage(query.getPage());
			tableData.setLimit(query.getLimit());
		}else {
			tableData.setPage(1);	
		}
		return tableData;
	}

	@Override
	public void delete(YunContext yunContext, Table table, Map<String, Object> dataMap) {
		// TODO Auto-generated method stub
		permissionUtil.hasPermission(table,"delete");
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_BEFORE_DELETE);
		Query query=new Query();
		Object keyValue=dataMap.get(table.getPrimaryKeyColumn().getColumnName());
		query.add(Restrictions.eq(table.getPrimaryKeyColumn().getColumnName(), keyValue));
		ITableService tableService=tableServiceStore.getTableService();
		tableService.delete(yunContext, table, query);
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_AFTER_DELETE);
		modelAccessLoggingUtil.logDataDelete(yunContext,table);
	}

	@Override
	public void insert(YunContext yunContext, Table table, Map<String, Object> dataMap) {
		// TODO Auto-generated method stub
		setInsertDefaultValue(yunContext,table,dataMap);
		validatorUtil.validate(yunContext, table, dataMap);
		permissionUtil.hasPermission(table,"insert");
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_BEFORE_INSERT);
		ITableService tableService=tableServiceStore.getTableService();
		Object keyValue=tableService.insert(yunContext, table, dataMap);
		dataMap.put(table.getPrimaryKeyColumn().getColumnName(), keyValue);
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_AFTER_INSERT);
		
		columnChangeLoggingUtil.logDataInsert(yunContext, table, dataMap);
		 modelAccessLoggingUtil.logDataInsert(yunContext, table,(Long)dataMap.get(table.getPrimaryKeyColumn().getColumnName()));
	}

	protected void setInsertDefaultValue(YunContext yunContext, Table table, Map<String, Object> dataMap) {
		UserContext userContext=UserContext.getCurrentUserContext();
		Column column=table.getColumnByColumnName("res_org_id");
		Object value=dataMap.get("res_org_id");
		if(column!=null && value==null) {
			dataMap.put("res_org_id", userContext.getResOrgId());
		}
		column=table.getColumnByColumnName("create_by");
		value=dataMap.get("create_by");
		if(column!=null) {
			dataMap.put("create_by", userContext.getResUserId());
		}
		column=table.getColumnByColumnName("update_by");
		value=dataMap.get("update_by");
		if(column!=null) {
			dataMap.put("update_by", userContext.getResUserId());
		}
		column=table.getColumnByColumnName("create_date");
		value=dataMap.get("create_date");
		if(column!=null) {
			dataMap.put("create_date", new Timestamp(System.currentTimeMillis()));
		}
		column=table.getColumnByColumnName("update_date");
		value=dataMap.get("update_date");
		if(column!=null) {
			dataMap.put("update_date", new Timestamp(System.currentTimeMillis()));
		}
		column=table.getColumnByColumnName("active");
		value=dataMap.get("active");
		if(column!=null && value==null) {
			dataMap.put("active",true);
		}
		column=table.getColumnByColumnName("version");
		value=dataMap.get("version");
		if(column!=null && value==null) {
			dataMap.put("version",1L);
		}
	}
	
	protected void setUpdateDefaultValue(YunContext yunContext, Table table, Map<String, Object> dataMap) {
		UserContext userContext=UserContext.getCurrentUserContext();
		Column column=table.getColumnByColumnName("update_by");
		if(column!=null) {
			dataMap.put("update_by", userContext.getResUserId());
		}
		column=table.getColumnByColumnName("update_date");
		if(column!=null) {
			dataMap.put("update_date", new Timestamp(System.currentTimeMillis()));
		}
	}
	
	@Override
	public void update(YunContext yunContext, Table table, Map<String, Object> dataMap) {
		// TODO Auto-generated method stub
		setUpdateDefaultValue(yunContext, table, dataMap);
		validatorUtil.validate(yunContext, table, dataMap);
		permissionUtil.hasPermission(table,"update");
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_BEFORE_UPDATE);
		
		Map<String,Object> oldValueMap=null;
		if(table.getLoggingColumnList().size()>0) {
			Object keyValue=dataMap.get(table.getPrimaryKeyColumn().getColumnName());
			Query query=new Query();
			query.add(Restrictions.eq(table.getPrimaryKeyColumn().getColumnName(), keyValue));
			ITableService tableService=tableServiceStore.getTableService();
			oldValueMap=tableService.select(yunContext, table, null, query).getData().get(0);
		}
		
		Query query=new Query();
		Object keyValue=dataMap.get(table.getPrimaryKeyColumn().getColumnName());
		query.add(Restrictions.eq(table.getPrimaryKeyColumn().getColumnName(), keyValue));
		ITableService tableService=tableServiceStore.getTableService();
		tableService.update(yunContext, table, dataMap, query);
		tableCURDRuleUtil.doRule(yunContext, table, dataMap, TableCRUDRuleUtil.TABLE_CURD_AFTER_UPDATE);
		
		Map<String,Object> newValueMap=null;
		if(table.getLoggingColumnList().size()>0) {
			Query newValueQuery=new Query();
			newValueQuery.add(Restrictions.eq(table.getPrimaryKeyColumn().getColumnName(), keyValue));
			newValueMap=tableService.select(yunContext, table, null, query).getData().get(0);
		}
		if(oldValueMap!=null&&newValueMap!=null) {
			columnChangeLoggingUtil.logDataUpdate(yunContext, table, oldValueMap, newValueMap);
		}
		 modelAccessLoggingUtil.logDataUpdate(yunContext, table,(Long)dataMap.get(table.getPrimaryKeyColumn().getColumnName()));
	}

}
